package com.monster.mechanic.dbmodel.hibernate;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathFactory;

import org.hibernate.cfg.Configuration;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import com.monster.mechanic.utils.constants.ResourcesConstants;
import com.monster.mechanic.utils.format.ExceptionUtil;
import com.monster.mechanic.utils.resources.HibernateConfigProperties;
import com.monster.mechanic.utils.resources.HibernateConfigPropertiesTest;
import com.monster.mechanic.utils.resources.Paths;

/**
 * Obtain the Hibernate configurations from properties files and read and load
 * the classes which will be annotated
 * 
 * @author ISAY ARENAS SOTO
 * @email isay.arenas@monster.com.mx
 */
public class HbmConfigAnnotations {

	private static final String HIBERNATE_MAPPING_FILE = "hbmconfig/mapping.xml";
	private static final String KEY_CONNECTION = "/connection/session-factory/property";
	private static final String KEY_CFG_BASE = "session-factory";
	private static final String CONFIGURATION = "hbmconfig";
	private static final String CLASS_EXT = ".class";

	private Document fuenteXml;

	public Document getFuenteXml() {
		return fuenteXml;
	}

	public void configure() throws ParserConfigurationException, SAXException,
			IOException {
		String path = Paths.getPathClass(getClass());
		String hibernateCfg = path.concat(HIBERNATE_MAPPING_FILE);
		DocumentBuilderFactory fabrica = DocumentBuilderFactory.newInstance();
		DocumentBuilder builder = fabrica.newDocumentBuilder();
		this.fuenteXml = builder.parse(new File(hibernateCfg));
		build();
	}

	private void build() {
		DocumentBuilderFactory factory = null;
		DocumentBuilder builder = null;
		String path = null;
		try {
			factory = DocumentBuilderFactory.newInstance();
			builder = factory.newDocumentBuilder();
			path = Paths.getPathClass(getClass());
			String nameXml = HibernateConfigProperties.getInstance()
					.getServerProperty("hibernate.connection");
			File file = new File(path.concat(CONFIGURATION)
					.concat(File.separator).concat(nameXml.trim()));
			if (file.exists()) {
				Document dmls = builder.parse(file.toURI().toString());
				loadElements(dmls, KEY_CFG_BASE, KEY_CONNECTION);
			} // if
			else {
				throw new RuntimeException("No existe el archivo [".concat(
						nameXml).concat("]"));
			}
		} catch (Exception e) {

		}
	}

	private void loadElements(Document dmls, String key, String children) {
		try {
			XPath xpath = XPathFactory.newInstance().newXPath();
			NodeList items = (NodeList) xpath.evaluate(children, dmls,
					XPathConstants.NODESET);
			if (items != null && items.getLength() > 0) {
				Element first = getFirstElement(key);
				for (int x = 0; x < items.getLength(); x++) {
					first.appendChild(this.fuenteXml.importNode(items.item(x),
							true));
				} // for x
			} // if
		} // try
		catch (Exception e) {

		} // catch
	}

	private Element getFirstElement(String key) {
		Element regresar = null;
		try {
			NodeList items = this.fuenteXml.getElementsByTagName(key);
			if (items.getLength() > 0) {
				regresar = (Element) items.item(0);
			} // for x
		} // try
		catch (Exception e) {
		} // catch
		return regresar;
	}

	public void buildMetaData(Configuration config) {
		File[] clazzes = null;
		String PATH_MODEL = null;
		try {
			String path = Paths.getPathClass(this.getClass()).concat(
					ResourcesConstants.MODEL_HIBERNATE);
			clazzes = toFilesDirectory(new File(path));
			PATH_MODEL = ResourcesConstants.MODEL_HIBERNATE.replace('/', '.');
			for (File file : clazzes) {
				String nameClazz = PATH_MODEL.concat(".")
						.concat(file.getName());
				nameClazz = nameClazz.substring(0, nameClazz.indexOf(".class"));
				Class clazz = Class.forName(nameClazz);
				config.addAnnotatedClass(clazz);
			}
		} // try
		catch (Exception e) {
			ExceptionUtil.mensaje(e);
		}
	}

	private File[] toFilesDirectory(File directory) {
		File[] files = null;
		if (directory.isDirectory()) {
			files = directory.listFiles(new FileFilter() {
				public boolean accept(File file) {
					return file.getName().endsWith(CLASS_EXT);
				}
			});
		}
		return files;
	}
}
